package dk.mga.androidExplore.gpsTracker;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;

import org.xmlpull.v1.XmlSerializer;

import dk.mga.androidExplore.R;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.Color;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationProvider;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.text.format.DateFormat;
import android.util.Log;
import android.util.Xml;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;

public class GPSTracker extends Activity implements LocationListener {
	
	private final String tag = "myLog";
	public final static String RECORDING_NAME = "RECORDINgNAME"; 
	private GPSLogService gpsLogService;
	private Intent serviceIntent;
	private String nameOnCurrentRecording;
	private boolean providerEnabled;
	private boolean startRequest = false;
	private DBHandler dbh;
	private final String noSelectionMade = "";

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		dbh = DBHandler.singleton(this);
    	setContentView(R.layout.gps_tracker);
    }

	@Override
	protected void onResume() {
		super.onResume();
		serviceIntent = new Intent(this, GPSLogService.class);
		startService(serviceIntent);// This will invoke onStartCommand() - if omitted the service will die with the client
		bindService(serviceIntent, gpsServiceConnection, Context.BIND_AUTO_CREATE);
		updateRecList();
		setSelectedRecordingLabel(noSelectionMade);
		View cv = findViewById(R.id.save_gpx_file);
		cv.setOnClickListener(recordingActionListener);
		cv = findViewById(R.id.send_gpx_file);
		cv.setOnClickListener(recordingActionListener);
		cv = findViewById(R.id.view_key_data);
		cv.setOnClickListener(recordingActionListener);
		cv = findViewById(R.id.delete_recording);
		cv.setOnClickListener(recordingActionListener);
	}

	@Override
	protected void onPause() {
		super.onPause();
		Log.d(tag, "GPS tracker on pause");
		gpsLogService.unregisterListener(this);
		unbindService(gpsServiceConnection);
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		super.onCreateOptionsMenu(menu);
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.gps_menu, menu);
		return true;
	}
	
	@Override
	public boolean onPrepareOptionsMenu(Menu menu) {
		menu.findItem(R.id.start).setEnabled(!gpsLogService.isLogging());
		menu.findItem(R.id.stop).setEnabled(gpsLogService.isLogging());
		return super.onPrepareOptionsMenu(menu);
	}
	
	private String createRecordingLabel() {
		Date d = new Date();
		String recording = "Trip"+DateFormat.format("ddMMMyyyy_kkmm", d);//ex Trip24dec.2010_1830
		return recording;
	}
	
	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		super.onOptionsItemSelected(item);
	    switch (item.getItemId()) {
	    case R.id.start:
	    	if (!gpsLogService.isLogging()){
	    		if (!providerEnabled) {
	    			enableGPSDialog().show();
	    		} else {
					startRecording();
	    		}
	    	}
	        break;
	    case R.id.stop:
	    	if (gpsLogService.isLogging()){
	    		gpsLogService.stopLogging(dbh);
	    	}
	    	break;
		case R.id.gps_terminate_button:
			gpsLogService.stopService(serviceIntent);
			finish();
			break;
		case R.id.resetDB:
			getCleanDatabase().show();
			break;
	    case R.id.settings:
	    	startActivity(new Intent(this, Prefs.class));
	    default:
	    	return false;
	    }
	    return true;
	}

	private AlertDialog enableGPSDialog() {
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setMessage(R.string.request_start_gps)
		       .setCancelable(true)
		       .setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
					@Override
		           public void onClick(DialogInterface dialog, int id) {
		                dialog.cancel();
		                GPSTracker.this.finish();
		           }
		       })
		       .setPositiveButton("Go to settings", new DialogInterface.OnClickListener(){
				@Override
				public void onClick(DialogInterface dialog, int which) {
					Log.d(tag, "Start settings");
					startActivity(new Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS));
					startRequest = true; // now startRecording must be called when connected to LogServer
				}
		       });
		return builder.create();
	}

	/**
	 * Private helper method to create a pop up dialog
	 * requesting a confirmation before delete contents of DB
	 * @return the Dialog object.
	 */
	private AlertDialog getCleanDatabase() {
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setMessage(R.string.CleanDatabase_label)
		       .setCancelable(true)
		       .setNegativeButton("Clean database (erase content)", new DialogInterface.OnClickListener() {
		           public void onClick(DialogInterface dialog, int id) {
			   			gpsLogService.stopService(serviceIntent);
			   			dbh.cleanDB();
		                dialog.cancel();
		           }
		       }).setPositiveButton("Cancel", new DialogInterface.OnClickListener() {
				public void onClick(DialogInterface dialog, int which) {
	                dialog.cancel();
				}
		       });
		return builder.create();
	}

	private ServiceConnection gpsServiceConnection = new ServiceConnection() {

		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			gpsLogService = ((GPSLogService.GPSLogBinder) service).getService();
			Log.d(tag, "Connected and bound");
			gpsLogService.registerListener(GPSTracker.this);
			if (startRequest && !gpsLogService.isLogging()) {
				startRecording();
			}
		}

		@Override
		public void onServiceDisconnected(ComponentName name) {
			Log.d(tag, "disconnected");
			gpsLogService.unregisterListener(GPSTracker.this);
		}
		
	};

	private AlertDialog deleteRecording(final String recording){
		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setMessage(R.string.GPS_handle_recording)
		       .setCancelable(true)
		       .setNegativeButton("Delete "+recording, new DialogInterface.OnClickListener() {
					@Override
		           public void onClick(DialogInterface dialog, int id) {
						DBHandler.singleton(GPSTracker.this).removeFromDB(recording);
						updateRecList();
		                dialog.cancel();
		           }
		       })
		       .setPositiveButton("Cancel", new DialogInterface.OnClickListener(){
				@Override
				public void onClick(DialogInterface dialog, int which) {
	                dialog.cancel();
					}
		       });
		return builder.create();
	}
	
	private OnClickListener recordingListListener = new OnClickListener() {
		@Override
		public void onClick(View v) {
			setSelectedRecordingLabel(v.getContentDescription().toString());
			((RadioButton)v).toggle();
		}
		
	};
	
	private String fileNameSpace = "/sdcard/dk.mga.androidExplore/";
	private String fileExt = ".gpx";
	
	private OnClickListener recordingActionListener = new OnClickListener(){

		@Override
		public void onClick(View v) {
			RadioGroup rg = (RadioGroup) findViewById(R.id.GPSRecordingsRadioGroup);
			View selected = findViewById(rg.getCheckedRadioButtonId());
			if (selected == null) return;
			switch (v.getId()){
			case R.id.view_key_data:
				Intent i = new Intent(GPSTracker.this, GPSRecordingDetails.class);
				i.putExtra(RECORDING_NAME, selected.getContentDescription());
				startActivity(i);
				break;
			case R.id.save_gpx_file:
				saveGPXFile(selected.getContentDescription().toString());
				break;
			case R.id.send_gpx_file:
				Intent intent = new Intent(Intent.ACTION_SEND);
				intent.setType("plain/text");
				String receiver = Prefs.getDefaultRecipient(GPSTracker.this);
				Log.d(tag, "Reciever: "+receiver);
				intent.putExtra(Intent.EXTRA_EMAIL, new String[]{receiver});
				intent.putExtra(Intent.EXTRA_SUBJECT, GPSTracker.this.getString(R.string.MAIL_SUBJECT)+selected.getContentDescription());
				intent.putExtra(Intent.EXTRA_TEXT, GPSTracker.this.getString(R.string.MAIL_BODY_TEXT));
				if (!new File(fileNameSpace+selected.getContentDescription()+fileExt).exists()) {
					saveGPXFile(selected.getContentDescription().toString());
				}
				String s = "file://"+fileNameSpace+selected.getContentDescription()+fileExt;
				intent.putExtra(Intent.EXTRA_STREAM, Uri.parse(s));
				startActivity(Intent.createChooser(intent, "Send file"));
				break;
			case R.id.delete_recording:
				GPSTracker.this.deleteRecording(selected.getContentDescription().toString()).show();
				break;
			default:
				break;
			}
	    	updateRecList();
			rg.clearCheck();
			setSelectedRecordingLabel(noSelectionMade);
		}
	};

	private void saveGPXFile(String fileName) {
		DBHandler dbh = DBHandler.singleton(this);
		File dir = new File(fileNameSpace);
		if (!dir.exists()) {
			boolean suc = dir.mkdir();
			if (suc) {
				Log.d(tag, "Created dir "+fileNameSpace);
			} else {
				Log.d(tag, "Dir not created! returning");
				return;
			}
		}
		try {
			FileOutputStream sdFileOS = new FileOutputStream(fileNameSpace+fileName+fileExt);
			writeGPXFile(dbh.getLocations(fileName), sdFileOS);
		} catch (FileNotFoundException e) {
			Log.e(tag, "FileNotFound", e);
		}
	}
		
	private void updateRecList() {
		ArrayList<String> list = DBHandler.singleton(this).getRecordings();
		if (list != null) {
			RadioGroup sv = (RadioGroup) findViewById(R.id.GPSRecordingsRadioGroup);
			sv.removeAllViews();
			Iterator<String> si = list.iterator();
			while(si.hasNext()) {
				RadioButton b = new RadioButton(this);
				String s = si.next();
				b.setContentDescription(s);
				b.setOnClickListener(recordingListListener);
				b.setText(s);
				b.setBackgroundColor(Color.DKGRAY & 0x80FFFFFF); // (transparent) make bg visible  
				sv.addView(b);
			}
		}
	}

	@Override
	public void onLocationChanged(Location loc) {
		Log.d(tag, "Location changed "+loc.toString());
		if (loc.hasSpeed()) {
			TextView speed = (TextView) findViewById(R.id.speed_view_id);
			speed.setText("Speed: "+ DBHandler.fromMpStoKMpH(loc.getSpeed()));
		}
//		Float delta = loc.getExtras().getFloat(GPSLogService.DELTA_DISTANCE);
//		Float distance = loc.getExtras().getFloat(GPSLogService.DISTANCE);

		if (loc.hasAltitude()) {
			TextView altitude = (TextView) findViewById(R.id.Altitude_view_id);
			altitude.setText("Elevation: "+altitude);
		}

		if (gpsLogService.isLogging()) {
		}
	}

	@Override
	public void onProviderDisabled(String arg0) {
		providerEnabled = false;
		Log.d(tag, arg0+" provider disabled");
	}

	@Override
	public void onProviderEnabled(String provider) {
		providerEnabled = true;
		Log.d(tag, provider+" provider enabled");	
	}

	private void startRecording() {
		nameOnCurrentRecording = createRecordingLabel();
		dbh.setRecording(nameOnCurrentRecording);
		gpsLogService.startLogging(dbh);
	}

	@Override
	public void onStatusChanged(String arg0, int arg1, Bundle arg2) {
		Log.d(tag, "Status changed "+arg0+" Status: "
				+((arg1==LocationProvider.AVAILABLE)?"Awailable":"Unavailable")
				+" satelites # "+arg2.getInt("satellites"));
		TextView tv = (TextView) findViewById(R.id.satellites_cnt_text);
		tv.setText("Satellites # "+arg2.getInt("satellites"));
	}

	private void writeGPXFile(Iterator<Location> locations, FileOutputStream fileOS) {
		final String namespace = "";
		final String gpx="gpx";
		final String time="time";
		final String trk="trk";
		final String name="name";
		final String trkseg="trkseg";
		final String trkpt="trkpt";
		final String desc="desc";
		final String ele="ele";
		final SimpleDateFormat sdf = new SimpleDateFormat("EEEE, dd MMM yyyy HH:mm:ss Z");
		

		XmlSerializer xs = Xml.newSerializer();
		try {
			Log.d(tag, "Writing to file");
			xs.setOutput(fileOS, null);
			xs.startDocument("UTF-8", true);
			xs.startTag(namespace, gpx);
			xs.attribute(namespace, "version", "1.0");
			xs.attribute(namespace, "creator", "Android Explore");
			xs.attribute(namespace, "xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
			xs.attribute(namespace, "xmlns", "http://www.topografix.com/GPX/1/0");
			xs.attribute(namespace, "xsi:schemaLocation", "http://www.topografix.com/GPX/1/0 http://www.topografix.com/GPX/1/0/gpx.xsd");
			xs.startTag(namespace, time);
			xs.text(sdf.format(new Date()));
			xs.endTag(namespace, time);
				xs.startTag(namespace, trk);
				xs.startTag(namespace, name);
				xs.endTag(namespace, name);
					xs.startTag(namespace, trkseg);
					while (locations.hasNext()) {
						Location l = locations.next();
						xs.startTag(namespace, trkpt);
						xs.attribute(namespace, "lat", ""+l.getLatitude());
						xs.attribute(namespace, "lon", ""+l.getLongitude());
		
							xs.startTag(namespace, time);
							xs.text(sdf.format(new Date(l.getTime())));
							xs.endTag(namespace, time);
		
							xs.startTag(namespace, desc);
							xs.text(""+(Math.round(l.getSpeed()*360)/100f)+" km/h");
							xs.endTag(namespace, desc);
		
							xs.startTag(namespace, ele);
							xs.text(""+l.getAltitude());
							xs.endTag(namespace, ele);
							
						xs.endTag(namespace, trkpt);
					}
					xs.endTag(namespace, trkseg);
				xs.endTag(namespace, trk);
			xs.endTag(namespace, gpx);
			xs.endDocument();
		} catch (IOException e) {
			Log.d(tag, "IOException when writing GPX file "+e.getMessage());
			e.printStackTrace();
		} finally {// we try to do our best
			try {
				fileOS.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}

	private void setSelectedRecordingLabel(String text) {
		TextView selctRecording = (TextView) findViewById(R.id.RecordingSelected_id);
		selctRecording.setText(text);
	}
}
